

function Validator(form) {
  this.form = form
  this.config = {
    info: {
      calssName: '',
      type: 'span',
      text: ''
    },
    debug: true
  }

  this._container = {}
  this._data = {}
  this._hook = {}
}


Validator.prototype.add = function (name, validates) {
  var
    form = this.form,
    container = this._container,
    info = this.config.info,
    hook = this._hook,
    __strategy = this.__strategy,
    __async = this.__async,
    el = form[name],
    error = this._error.bind(this),
    field

  // 参数验证
  typeof name === 'string' || error('add(>name<, validates) name 必须是一个 "string" 类型的值')
  el || error('add(>name<, validates) ' + name + '字段不存在')
  Array.isArray(validates) || error('add(name, >validates<) validates 必须是一个 "array" 类型的值')
  validates.length > 0 || error('add(name, >validates<) ' + name + '字段未指定任何有效的验证策略')

  // 初始化字段
  field = container[name] = {
    name: name,
    el: el,
    required: false,
    disabled: false,
    infoEl: undefined,
    infoMsg: '',
    pass: null,
    value: '',
    strategy: [],
    async: [],
  }


  Object.defineProperties(field, {
    'value': {
      get: function () {
        return field.el.value
      },
      set: function (val) {
        field.el.value = val
      }
    },

    'infoMsg': {
      get: function () {
        return field.infoEl.innerText
      },
      set: function (val) {
        field.infoEl.innerText = val
      }
    }
  })

  // 创建错误元素
  if (typeof info === 'function') {
    field.infoEl = info(el, name)
  } else {

    var
      className = info.className,
      type = info.type,
      text = info.text

    el.insertAdjacentHTML('afterend', '<' + type + ' class="' + className + '">' + text + '</' + type + '>')
    field.infoEl = el.nextElementSibling
  }

  // 封装策略验证
  validates.forEach(function (validate) {
    var
      args = validate.slice(),
      strategy = args.shift(),
      err = args.pop(),
      self = this


    if (strategy in __strategy) {
      field.strategy.push(function () {
        return __strategy[strategy].apply(self, [].concat(field, err, args))
      })
    } else if (strategy in __async){
      field.async.push(function () {
        var arg = args.slice()
        return __async[strategy].apply(self, [].concat(field, arg.pop(), err, arg))
      })
    } else {
       error('add(name, >validates<)' + name + '字段上的' + strategy + '验证策略不存在')
    }

  }, this)

  // registered 钩子
  // 此时，字段已注册，所有验证策略已载入，字段对象已生成
  'registered' in hook && hook.registered(field)

  return this
}


Validator.prototype.result = function (name) {
  var
    container = this._container,
    data = this._data,
    hook = this._hook,
    allAsync = {},    // 存放字段的异步验证策略
    pass = [],        // 存放字段的验证结果
    isEmptyObj = this._isEmptyObj.bind(this),
    self = this,
    field

  if (typeof name === 'string') {

    (field = container[name]) || error('result(>name<) ' + name + '字段尚未注册')

    // 获取单字段的验证结果
    pass = iteration(field)

  } else {

    for (var key in container) {
      var save = iteration(container[key])

      // 保存所有已注册字段的验证结果
      pass.push(save)
    }

    // 获取整个表单的验证结果
    pass = pass.every(Boolean)
  }

  return isEmptyObj(allAsync)
          ? pass
            ? allSyncIteration(allAsync)
            : fieldHook(pass, field) || formHook(false) || Promise.reject()
          : fieldHook(pass, field) || formHook(pass) || pass


  function iteration(field) {
    

    var
      strategy = field.strategy,
      fieldName = field.name,
      err

  
    field.infoMsg = ''              // 清空错误
    data[fieldName] = field.value    // 存储值
    field.pass = false              // 初始化 pass 验证值

    // 提取字段的异步验证策略
    // 当所有普通验证策略通过后， 才由 Promise.all 方法一并执行异步验证策略
    field.async.length && (allAsync[fieldName] = field.async)
  
    for(var i = 0, fn; fn = strategy[i++];) {

      // 获取错误消息
      err = fn()

      // 禁止验证
      // 手动改为通过
      // 必填字段
      if (field.disabled || field.pass || (!field.required && field.value === '')) {
        return field.pass = true
      }

      // 一旦， 普通验证策略产生了错误消息就终止整个迭代
      if (err) {
        field.infoMsg = err
        return field.pass = false
      }
    }

    return field.pass = true
  }

  function allSyncIteration (allAsync) {
    var all = []    // 存放所有 promise 实例

    // 单字段的异步整合
    // 显示成功消息和错误消息
    for (var key in allAsync) {

      (function (key) {
        var field = container[key]
        field.pass = false
        Promise.all(allAsync[key].map(function (fn) {
          // 执行异步验证策略
          // 获取 promise 实例
          var one = fn()
          all.push(one)
          return one
        }))
          // 字段验证未/已通过的钩子
          // 字段存在异步验证策略时
          .then(
            function (msg) {
              self.setInfo(key, msg[0])
              field.pass = true
              fieldHook(true,field)
            },
            function (err) {
              self.setInfo(key, err)
              field.pass = false
              fieldHook(false, field)
            })
      })(key)
    }

    // 整个表单验证未/已通过的钩子
    // 存在异步策略函数
    Promise.all(all).then(function () {
      formHook(true)
    }, function () {
      formHook(false)
    })

    // 整儿表单的异步整合
    // 返回一个 Promise 实例
    return Promise.all(all)
  }


  function formHook (pass) {
    if (typeof name !== 'string') {
      pass
      ? 'succeed' in hook && hook.succeed(container)
      : 'failed' in hook && hook.failed(container)
    }
  }

  function fieldHook (pass, field) {
    if (typeof name === 'string') {
      pass
        ? 'fieldSucceed' in hook && hook.fieldSucceed(field)
        : 'fieldFailed' in hook && hook.fieldFailed(field)
    } else {
      for (var key in container) {
        field = container[key]
        pass
          ? 'fieldSucceed' in hook && hook.fieldSucceed(field)
          : 'fieldFailed' in hook && hook.fieldFailed(field)
      }
    }
  }
}


Validator.prototype.getData = function () {
  var
    data = this._data,
    result = JSON.parse(JSON.stringify(data))


    if (!arguments.length) {
      return result
    }

    [].forEach.call(arguments, function (e) {
      if (e in result) {
        delete result[e]
      }
    })

    return result
}


// 生命周期钩子


Validator.prototype.hook = function (key, value) {
  return this._merge(this._hook, key, value, true, {
    fn: this._isFunction,
    typeErr: '必须是一个函数'
  })
}

// 快捷事件方法

Validator.prototype.oninput = function (fn) {
  var
    form = this.form,
    container = this._container,
    error = this._error.bind(this),
    self = this

  fn = fn || function () {}

  typeof fn === 'function' || error('oninput(>fn<) 必须是一个函数')

  form.addEventListener('input', function(e) {
    var
      name = e.target.name,
      result

    if (!(name in container)) {
      return
    }
    if ((result = self.result(name)) instanceof Promise ) {
      result.then(function () {
        fn.call(self, e)
      }, function () {})
    } else {
      result && fn.call(self, e)
    }

  }, false)

  return this
}


Validator.prototype.onsubmit = function (fn) {
  var
    form = this.form,
    container = this._container,
    error = this._error.bind(this),
    self = this

  fn = fn || function () {}

  typeof fn === 'function' || error('oninput(>fn<) 必须是一个函数')

  form.addEventListener('submit', function(e) {
    var result

    e.preventDefault()

    if ((result = self.result()) instanceof Promise ) {
      result.then(function () {
        fn.call(self, e)
      }, function () {})
    } else {
      result && fn.call(self, e)
    }

  }, false)

  return this

}


// 内置方法

Validator.prototype.set = function (name, key, value) {
  this._container[name][key] = value
}

Validator.prototype.setInfo = function (name, value) {
  this.set(name, 'infoMsg', value)
}

Validator.prototype.setValue = function (name, value) {
  this.set(name, 'value', value)
}

Validator.prototype.setPass = function (name, value) {
  this.set(name, 'pass', value)
}

Validator.prototype.strategy = function (name, strategy, force) {
  return this._merge.call(this, this.__strategy, name, strategy, force, {
    fn: this._isFunction,
      typeErr: '必须是一个函数',
      repeatErr: '普通验证策略',
  })
}

Validator.prototype.async = function (name, strategy, force) {
  return this._merge.call(this, this.__async, name, strategy, force, {
    fn: this._isFunction,
    typeErr: '必须是一个函数',
    repeatErr: '异步验证策略',
  })
}

Validator.prototype.pattern = function (name, pattern, force) {
  return this._merge.call(this, this.__pattern, name, pattern, force, {
    fn: this._isRegexp,
    typeErr: '必须是一个正则表达式',
    repeatErr: '正则表达式',
  })
}


// 私有方法

Validator.prototype._error = function (err) {
  var 
    debug = this.config.debug,
    hook = this._hook

  // error 错误钩子
  if ('error' in hook) {
    hook.error(err)
    return
  }

  if (debug) {
    throw new Error(err)
  }

}

Validator.prototype._type = function (obj, str) {
  return Object.prototype.toString.call(obj) === '[object '+ str +']'
}

Validator.prototype._isEmptyObj = function (obj) {
  if (!this._type(obj, 'Object')) {
    return false
  }

  for (var key in obj) {
    return true
  }

  return false
}

Validator.prototype._isRegexp = function (obj) {
  return this._type(obj, 'RegExp')
}

Validator.prototype._isFunction = function (obj) {
  return this._type(obj, 'Function')
}



Validator.prototype._merge = function (obj, key, value, force, options) {
  var
    error = this._error.bind(this),
    source = obj,
    target = {},
    fn = options.fn.bind(this),
    typeErr = options.typeErr,
    repeatErr = options.repeatErr || ''
  typeof key === 'string'
    ? target[key] = value
    : (target = key) && (force = value)

  for (var key in target) {
    force
      ? fn(target[key])
        ? source[key] = target[key]
        : error(key + typeErr)
      : key in source
        ? error('名为 ' + key + repeatErr + '已存在 （您可指定第三个参数为 true，强制覆盖）')
        : fn(target[key])
          ? source[key] = target[key]
          : error(key + typeErr)
  }
  return this
}



// 验证策略

Validator.prototype.__strategy = {

  notEmpty: function (field, err) {
    field.required = true
    err = err || '*'
    if (field.value === '') {
      return err
    }
  },

  disabled: function (field, err) {
    field.disabled = true
    return err
  },
  minLength: function (field, err, length) {
    if (field.value.length < length) {
      return err
    }
  },

  maxLength: function (field, err, length) {
    if (field.value.length > length) {
      return err
    }
  },

  min: function (field, err, number) {
    if (field.value < number) {
      return err
    }
  },

  max: function (field, err, number) {
    if (field.value > number) {
      return err
    }
  },

  equal: function (field, err, name) {
    if (field.value !== this.form[name].value) {
      return err
    }
  },

  test: function () {
    var
      args = Array.prototype.slice.call(arguments),
      field = args.shift(),
      err = args.shift(),
      __pattern = this.__pattern,
      error = this._error.bind(this),
      regexp

    for (var i = 0, name; name = args[i++];) {
      (regexp = __pattern[name]) || error('未找到名为' + name + '的正则表达式，您可以通过实例的 pattern 方法扩展它');
      if (!regexp.test(field.value)) {
        return err
      }
    }
  },

  rawTest: function(field, err, pattern) {
    if (!pattern.test(field.value)) {
      return err
    }
  }
}

// 异步验证策略

Validator.prototype.__async = {
 
}


// 正则库
 
Validator.prototype.__pattern = {
  email: /^[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?$/,
  phone: /^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\d{8}$/,
  id: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
  qq: /^[1-9][0-9]{4,10}$/,
  // 微信号
  weChat: /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/,
  zipCode: /^[1-9]d{5}(?!d)^/
}


module.exports = Validator